package com.budwk.app.miniapp.services.impl;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.budwk.app.exception.ServiceException;
import com.budwk.app.miniapp.common.CacheConstants;
import com.budwk.app.miniapp.common.ConfigConstant;
import com.budwk.app.miniapp.models.WxEvent;
import com.budwk.app.miniapp.models.WxUser;
import com.budwk.app.miniapp.models.WxUserRelation;
import com.budwk.app.miniapp.services.WxUserAuthService;
import com.budwk.app.miniapp.services.WxUserService;
import com.budwk.app.miniapp.services.WxeventService;
import com.budwk.app.miniapp.services.WxuserrelationService;
import com.budwk.app.miniapp.utils.*;
import com.budwk.starter.common.constant.RedisConstant;
import com.budwk.starter.common.exception.BaseException;
import com.budwk.starter.database.service.BaseServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.nutz.aop.interceptor.async.Async;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;

import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

@IocBean(args = {"refer:dao"})
public class WxUserAuthServiceImpl extends BaseServiceImpl<WxUser> implements WxUserAuthService {
    public WxUserAuthServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private WxUserService wxUserService;

    @Inject
    private WxuserrelationService wxuserrelationService;

    @Inject
    private PropertiesProxy propertiesProxy;

    @Inject
    private RedisService redisService;

    @Inject
    private WxeventService wxeventService;

    //登录 返回user
    @Override
    @Aop(TransAop.READ_COMMITTED)
    public WxUser wxLogin(String loginCode, String phoneCode, String phone, String apptype) {
        WxUser wxUser = null;
        String appid = propertiesProxy.get("wx.ma.configs.appId");
        String secret = propertiesProxy.get("wx.ma.configs.secret");
        try {
            if (null != loginCode) {
                wxUser = this.loginWx(apptype, loginCode, apptype);
            }
            wxUser = getPhoneByWx(wxUser, phoneCode, appid, secret);
            if (StringUtils.isBlank(wxUser.getPhone())) {
                throw new Exception("获取电话号码异常！");
            }
            WxUser wb = new WxUser();
            Cnd cnd = Cnd.NEW();
            cnd.and("phone", "=", wxUser.getPhone());
            List<WxUser> nlist = wxUserService.query(cnd);
            if (nlist.size() < 1) {
                //新增微信用户
                try {
                    dao().insert(wxUser);
                } catch (Exception e) {
                    if (e.getMessage().contains("uk_appid_openid")) {
                        wxUser = this.fetch(Cnd.where("open_id", "=", wxUser.getOpenId()));
                    }
                }
            } else {
                wxUser.setAppType(null);
                this.update(wxUser);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return wxUser;
    }

    @Override
    public WxUser refreshToken(String refreshToken) {
        if(!JwtUtil.validate(refreshToken)){
          throw new ServiceException("refreshToken无效");
        }
        cn.hutool.json.JSONObject r=JwtUtil.getJSONObject(refreshToken);
        WxUser user= JSONUtil.toBean(r,WxUser.class);
        WxUser userGet = this.fetch(user.getId());
        userGet = refreshToken(userGet);
        return userGet;
    }

    /*
     * 微信小程序登录
     * */
    @Aop(TransAop.READ_COMMITTED)
    public WxUser loginWx(String configName, String jsCode, String apptype) throws Exception {
        WxUser wxUser = new WxUser();
        String appid = propertiesProxy.get("wx.ma.configs.appId");
        String secret = propertiesProxy.get("wx.ma.configs.secret");
        if (null == appid || null == secret) {
            System.out.println("配置文件有误");
        }
        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
        JSONObject requestUrlParam = new JSONObject();
        //小程序appId
        requestUrlParam.put("appid", appid);
        //小程序secret
        requestUrlParam.put("secret", secret);
        //小程序端返回的code
        requestUrlParam.put("js_code", jsCode);
        //默认参数
        requestUrlParam.put("grant_type", "authorization_code");
        try {
            String loginresult = HttpUtil.post(requestUrl, requestUrlParam);
            JSONObject jsonObject = JSONObject.parseObject(loginresult);
            String openid = jsonObject.getString("openid");
            String sessionKey = jsonObject.getString("session_key");
            String unionid = jsonObject.getString("unionid");
            String errcode = jsonObject.getString("errcode");
            if (errcode != null) {
                if ("40029".equals(errcode)) {
                    throw new IllegalArgumentException(String.format("js_code无效!", appid));
                }
                if ("45011".equals(errcode)) {
                    throw new IllegalArgumentException(String.format("API 调用太频繁，请稍候再试!", appid));
                }
                if ("40226".equals(errcode)) {
                    throw new IllegalArgumentException(String.format("高风险等级用户，小程序登录拦截 。风险等级详见用户安全解方案!", appid));
                }
                if ("-1".equals(errcode)) {
                    throw new IllegalArgumentException(String.format("系统繁忙，请稍候再试!", appid));
                }
            }
            Cnd cnd = Cnd.NEW();
            cnd.and("open_id", "=", openid);
            wxUser = this.fetch(cnd);
            if (wxUser == null) {
                //新增微信用户
                wxUser = new WxUser();
                wxUser.setAppType(configName);
                wxUser.setOpenId(openid);
                wxUser.setSessionKey(sessionKey);
                wxUser.setUnionId(unionid);
                this.insert(wxUser);
            } else {
                //更新SessionKey
                wxUser.setAppType(ConfigConstant.WX_APP_TYPE_1);
                wxUser.setOpenId(openid);
                wxUser.setSessionKey(sessionKey);
                wxUser.setUnionId(unionid);
                this.update(wxUser);
            }
            setWxUserRelation(wxUser.getId(), wxUser.getOpenId(), apptype);
            setRedisCacheAllUserList();
            setRedisCacheEvent(wxUser);
            setUserRedis(wxUser);
            wxUser = setToken(wxUser);
        } catch (Exception e) {
            System.out.println("登陆异常" + e.getMessage());
            throw new Exception("登录异常:" + e.getMessage());
        }
        return wxUser;
    }

    //获取用户登录的手机号
    public WxUser getPhoneByWx(WxUser wxUser, String phoneCode, String appid, String secret) {
        if (phoneCode != null) {
            String accessToken = "";
            String phoneNumber = "";
            try {
                String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
                String loginresult = HttpUtils.getRequest(url);

                JSONObject jsonObject = JSONObject.parseObject(loginresult);
                accessToken = jsonObject.getString("access_token");

            } catch (Exception e) {
                System.out.println("获取access_token异常" + e.getMessage());
            }
            try {
                //用 access_token + code 获取手机号
                JSONObject json = new JSONObject();
                json.put("code", phoneCode);
                String resPhone = HttpUtil.post("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken, json.toString());
                if (org.apache.commons.lang3.StringUtils.isEmpty(resPhone) || !resPhone.contains("phone_info") || !resPhone.contains("phoneNumber")) {
                    System.out.println("微信官方修改了小程序获取用户手机号码相关接口！");
                }
                JSONObject resPhoneInfo = JSON.parseObject(resPhone);
                JSONObject phoneInfo = resPhoneInfo.getJSONObject("phone_info");
                phoneNumber = phoneInfo.get("phoneNumber").toString();
                if (StringUtils.isBlank(phoneNumber)) {
                    throw new BaseException("获取手机号异常！");
                }
                wxUser.setPhone(phoneNumber);
                this.update(wxUser); //更新手机号

            } catch (Exception e) {
                System.out.println("获取手机号异常" + e.getMessage());
            }
        }
        return wxUser;
    }

    /*
     * 获取token
     * */
    public String getAccessToken(String appid, String secret) {
        String accessToken = "";
        try {
            String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
            String loginresult = HttpUtils.getRequest(url);
            JSONObject jsonObject = JSONObject.parseObject(loginresult);
            accessToken = jsonObject.getString("access_token");
        } catch (Exception e) {
            System.out.println("获取access_token异常" + e.getMessage());
        }
        return accessToken;
    }

    public void setUserRedis(WxUser wxUser) {
        try {

            WxUser redisWxUser = RedisUtils.getCacheObject(CacheConstants.ONLINE_MINIAPP_USER_KEY + wxUser.getOpenId());
            if (redisWxUser == null) {
                RedisUtils.setCacheObject(CacheConstants.ONLINE_MINIAPP_USER_KEY + wxUser.getOpenId(), wxUser, Duration.ofDays(1));
            } else {
                RedisUtils.deleteObject(CacheConstants.ONLINE_MINIAPP_USER_KEY + wxUser.getOpenId());
                RedisUtils.setCacheObject(CacheConstants.ONLINE_MINIAPP_USER_KEY + wxUser.getOpenId(), wxUser, Duration.ofDays(1));
            }
        } catch (Exception e) {
            System.out.println("设置缓存出错" + e.getMessage());
        }
    }

    //设置token
    public WxUser setToken(WxUser wxUser) {
        try {
            this.loginByDevice(wxUser, "app");
            if (wxUser != null) {
                wxUser.setLoginToken(StpUtil.getTokenValue());
//                String token=StpUtil.getTokenValue();
//                redisService.sadd(RedisConstant.WS_ROOM+wxUser.getId(),RedisConstant.WS_TOKEN+token);
//                redisService.sadd(RedisConstant.WS_TOKEN+token,wxUser.getId());
                wxUser.setRefreshToken(JwtUtil.createToken(wxUser.getId(),wxUser.getPhone()));
            }
        } catch (Exception e) {
            System.out.println("设置缓存出错" + e.getMessage());
        }
        return wxUser;
    }

    public WxUser refreshToken(WxUser wxUser) {
        try {
            this.loginByDevice(wxUser, "app");
            if (wxUser != null) {
                wxUser.setLoginToken(StpUtil.getTokenValue());
            }
        } catch (Exception e) {
            System.out.println("设置缓存出错" + e.getMessage());
        }
        return wxUser;
    }

    /**
     * 登录系统 基于 设备类型
     * 针对相同用户体系不同设备
     *
     * @param wxUser 登录用户信息
     */
    public void loginByDevice(WxUser wxUser, String deviceType) {
        try {
            //String userid = "app_user:" + (wxUser.getId());
            String userid = wxUser.getId();
            SaHolder.getStorage().set("loginUser", wxUser);
            StpUtil.login(userid, deviceType);
            StpUtil.getTokenSession().set("loginUser", wxUser);//设置用户数据(多级缓存)
            System.out.println("111111111111111111111111111111111111111111111111"+StpUtil.getTokenValue());
            // 记录登录信息
       /*     LogininforEvent logininforEvent = new LogininforEvent();
            logininforEvent.setUsername(wxUser.getPhone());
            logininforEvent.setStatus("Success");
            logininforEvent.setMessage("小程序登陆");
            logininforEvent.setRequest(ServletUtils.getRequest());
            logininforEvent.setApptype("东方祥云小程序");
            NutzIocUtil.context().publishEvent(logininforEvent);*/
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("设置缓存出错" + e.getMessage());
        }
    }


    /**
     * 保存userid ，openid，apptype 关系表
     *
     * @param userid
     * @param openid
     * @param apptype
     */
    @Aop(TransAop.READ_COMMITTED)
    public void setWxUserRelation(String userid, String openid, String apptype) {
        try {

            Cnd cnd = Cnd.NEW();
            cnd.and("user_id", "=", userid)
                    .and("open_id", "=", openid)
                    .and("app_type", "=", apptype);
            List<WxUserRelation> urlist = wxuserrelationService.query(cnd);
            if (urlist.size() < 1) {
                WxUserRelation wuo = new WxUserRelation();
                wuo.setUserId(userid);
                wuo.setOpenId(openid);
                wuo.setAppType(apptype);
                wxuserrelationService.insert(wuo);
            }
        } catch (Exception e) {
            throw new BaseException("设置用户关系表出错" + e.getMessage());
        }
    }


    /**
     * 查询所有用户，缓存到redis
     */
    @Async
    public void setRedisCacheAllUserList() {
        try {
            List<WxUser> ulist = this.query();
            if (RedisUtils.getCacheObject(CacheConstants.WX_ALL_USER_KEY) == null) {
                RedisUtils.setCacheObject(CacheConstants.WX_ALL_USER_KEY, ulist, Duration.ofDays(1));
            } else {
                RedisUtils.deleteObject(CacheConstants.WX_ALL_USER_KEY);
                RedisUtils.setCacheObject(CacheConstants.WX_ALL_USER_KEY, ulist, Duration.ofDays(1));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //根据当前登录用户的
    //@Async
    public void setRedisCacheEvent(WxUser wxUser) {
        try {
            List<WxEvent> list = RedisUtils.getCacheList(CacheConstants.MINIAPP_EVENT_ALL_LIST_KEY);
            if (list != null && list.size() < 1) {
                Cnd cnd=Cnd.NEW();
                list = wxeventService.query(cnd);
                RedisUtils.setCacheList(CacheConstants.MINIAPP_EVENT_ALL_LIST_KEY, list);
            }
            List<WxEvent> newlist = new ArrayList<>();
            for (WxEvent vo : list) {
                if (vo.getLatitude() != null && vo.getLongitude() != null && wxUser.getLatitude() != null && wxUser.getLongitude() != null) {
                    //计算事件离我的距离
                    Double distance = DistanceUtils.latitudeLongitudeDistance(vo.getLatitude(), vo.getLongitude(), Double.valueOf(wxUser.getLatitude()), Double.valueOf(wxUser.getLongitude()));
                    vo.setDistanceForMe((distance));
                    newlist.add(vo);
                }
            }

            //以离我距离的远近顺序来排序，Comparator.comparing(WxEventVo::getDistanceForMe).reversed() 逆序
            List<WxEvent> eventlist = newlist.stream().sorted(Comparator.comparing(WxEvent::getDistanceForMe)).collect(Collectors.toList());
            List<WxEvent> reidslist = RedisUtils.getCacheList(CacheConstants.ONLINE_USER_EVENT_LIST_KEY + wxUser.getOpenId());
            if (reidslist == null) {
                RedisUtils.setCacheList(CacheConstants.ONLINE_USER_EVENT_LIST_KEY + wxUser.getOpenId(), eventlist);
            }
            {
                RedisUtils.deleteObject(CacheConstants.ONLINE_USER_EVENT_LIST_KEY + wxUser.getOpenId());
                RedisUtils.setCacheList(CacheConstants.ONLINE_USER_EVENT_LIST_KEY + wxUser.getOpenId(), eventlist);
                //缓存时间设置1天
                RedisUtils.expire(CacheConstants.ONLINE_USER_EVENT_LIST_KEY + wxUser.getOpenId(), Duration.ofDays(1));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
